﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace Batty
{
    public class Bat
    {
        private Pondhawk game;
        private Model model;
        public int leftwing;
        public int rightwing;
        private float wingAngle = 0;
        private bool flapping = false;
        /// <summary>
        /// Current position
        /// </summary>
        private Vector3 position = Vector3.Zero;

        /// <summary>
        /// Compass heading (radians, 0 is Z direction)
        /// </summary>
        private float azimuth = 0;

        /// <summary>
        /// Climb angle (radians, 0 is level)
        /// </summary>
        private float elevation = 0;

        /// <summary>
        /// How fast we are going (cm/sec)
        /// </summary>
        private float speed = 0;

        /// <summary>
        /// Flap in cm/sec^2
        /// </summary>
        private float flap = 0;

        /// <summary>
        ///  Decelleration due to drag
        /// </summary>
        private const float Drag = 1;

        /// <summary>
        /// Maximum flap (cm/sec^2)
        /// </summary>
        private const float MaxFlap = 300;

        /// <summary>
        /// The current turning rate in radians per second
        /// Effectively the azimuth change rate
        /// </summary>
        private float turnRate = 0;

        /// <summary>
        /// The maximum turning rate
        /// </summary>
        private const float MaxTurnRate = (float)Math.PI;

        /// <summary>
        /// The current turning rate in radians per second
        /// Effectively the azimuth change rate
        /// </summary>
        private float elevRate = 0;

        /// <summary>
        /// The maximum turning rate
        /// </summary>
        private const float MaxElevRate = (float)Math.PI;

        /// <summary>
        /// The current turning rate in radians per second
        /// Effectively the azimuth change rate
        /// </summary>
        private float bankRate = 0;

        /// <summary>
        /// The maximum turning rate
        /// </summary>
        private const float MaxBankRate = (float)Math.PI;

        /// <summary>
        /// Ship orientation as a quaternion
        /// </summary>
        Quaternion orientation = Quaternion.Identity;


        /// <summary>
        /// The current ship flap
        /// </summary>
        public float Flap { get { return flap; } set { flap = value; } }

        /// <summary>
        /// Turning rate in radians per second
        /// </summary>
        public float TurnRate { get { return turnRate; } set { turnRate = value; } }

        public float ElevRate { get { return elevRate; } set { elevRate = value; } }

        public float BankRate { get { return bankRate; } set { bankRate = value; } }

        public Vector3 Position { get { return position; } set { position = value; } }

        public bool Flapping { get { return flapping; } set { flapping = value; } }

        public Model Model { get { return model; } }

        /// <summary>
        /// The current ship transformation
        /// </summary>
        public Matrix Transform
        {
            get
            {
                return Matrix.CreateFromQuaternion(orientation) *
                        Matrix.CreateTranslation(position);
            }
        }


        public Bat(Pondhawk game)
        {
            this.game = game;
        }

        /// <summary>
        /// This function is called to load content into this component
        /// of our game.
        /// </summary>
        /// <param name="content">The content manager to load from.</param>
        public void LoadContent(ContentManager content)
        {
            model = content.Load<Model>("bat-rigid");
            leftwing = model.Bones.IndexOf(model.Bones["LeftWing1"]);
            rightwing = model.Bones.IndexOf(model.Bones["RightWing1"]);

        }

        /// <summary>
        /// This function is called to update this component of our game
        /// to the current game time.
        /// </summary>
        /// <param name="gameTime"></param>
        public void Update(GameTime gameTime)
        {

            float wingTime = .5f;

            if (flapping && wingAngle < .90f)
            {
                wingAngle += (float)(0.90 * gameTime.ElapsedGameTime.TotalSeconds / wingTime);
                if (wingAngle > 0.90f)
                    wingAngle = 0.90f;


            }
            else if (!flapping && wingAngle > 0)
            {
                wingAngle -= (float)(0.90 * gameTime.ElapsedGameTime.TotalSeconds / wingTime);
                if (wingAngle < 0)
                    wingAngle = 0;
            }

            float delta = (float)gameTime.ElapsedGameTime.TotalSeconds;

            float turnAngle = turnRate * MaxTurnRate * delta;
            orientation *= //Quaternion.CreateFromAxisAngle(new Vector3(0, 0, 1), -turnAngle) *
                           Quaternion.CreateFromAxisAngle(new Vector3(0, 1, 0), turnAngle);
            orientation *= Quaternion.CreateFromAxisAngle(new Vector3(1, 0, 0), ElevRate * MaxElevRate * delta);
            orientation.Normalize();

            //
            // Position updates
            //

            float acceleration = flap * MaxFlap - Drag * speed;
            speed += acceleration * (float)gameTime.ElapsedGameTime.TotalSeconds;

            Matrix transform = Matrix.CreateFromQuaternion(orientation);

            Vector3 directedFlap = Vector3.TransformNormal(new Vector3(0, 0, 1), transform);
            position += directedFlap * speed * (float)gameTime.ElapsedGameTime.TotalSeconds;
        }


        /// <summary>
        /// This function is called to draw this game component.
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="gameTime"></param>
        public void Draw(GraphicsDeviceManager graphics, GameTime gameTime)
        {

            DrawModel(graphics, model, Transform);
        }

        private void DrawModel(GraphicsDeviceManager graphics, Model model, Matrix world)
        {
            Matrix[] transforms = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(transforms);

            transforms[leftwing] = Matrix.CreateRotationY(wingAngle) * transforms[leftwing];
            transforms[rightwing] = Matrix.CreateRotationY(-wingAngle) * transforms[rightwing];

            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();
                    effect.World = transforms[mesh.ParentBone.Index] * world;
                    effect.View = game.Camera.View;
                    effect.Projection = game.Camera.Projection;
                }
                mesh.Draw();
            }
        }
    }
}
